<template>
  <div style="width: 100%">
    <!-- 如果mergeComProps.comType等于crud然后又没有boxType则表示为列表查询的搜索框 -->
    <el-input
      v-if="mergeComProps.comType === 'crud' && !boxType"
      v-bind="mergeInputProps"
      v-model="searchData"
      @input="handleSearchInput"
    ></el-input>
    <el-input
      v-else
      v-model="labelShows"
      v-bind="mergeInputProps"
      clearable
      readonly
      @click="openDialogTable"
      :disabled="disabled"
    >
      <template #suffix>
        <span v-if="!disabled"
          ><el-icon
            @click.stop="handleClear"
            v-if="labelShows"
            style="cursor: pointer"
            ><CircleCloseFilled
          /></el-icon>
          <el-icon v-else><ArrowDown /></el-icon
        ></span>
      </template>
    </el-input>
    <basic-dialog-table
      ref="basicDialogTableRef"
      v-model="visible"
      v-bind="mergeCrudProps"
      v-model:defaultSelectList="defaultSelectList"
      :selectedKey="mergeComProps.selectedKey"
      @submit="handleSubmit"
      @cancel="handleCancel"
    >
      <template #footer-left>
        <span style="text-align: left"
          >已选择
          <span style="color: #1e89ff">{{ defaultSelectList.length }}</span>
          条数据：
          <el-tag
            v-for="item in defaultSelectList"
            closable
            @close="handleClose(item)"
            >{{ item[mergeComProps.saveFieldMap[prop]] }}</el-tag
          ></span
        >
      </template>
    </basic-dialog-table>
  </div>
</template>

<script setup>
import { merge } from 'lodash'
import emitter from '@/utils/mitt.js'
import { computed, nextTick, watch } from 'vue'
import { apiGetBusTypeDialogList } from '@/apis/objectModel/businessType.js'
import { apiGetAttributeDialogList } from '@/apis/objectModel/attribute.js'
import { apiGetStatusMetaDialogList } from '@/apis/objectModel/statusMeta.js'
import { apiGetKeyReatDialogList } from '@/apis/uiEngine/codeRuleManage/keyReatRule.js'
import { apiRefer as apiGetUnitDialogList } from '@/apis/baseData/unitManage.js'
import { apiGetEnumTypeDialogList } from '@/apis/objectModel/enumType.js'
import { apiRefer as apiGetDepDialogList } from '@/apis/orgManage/depManage.js'
import { apiRefer as apiGetUserDialogList } from '@/apis/permission/userManage.js'

const that = getCurrentInstance()
const emits = defineEmits(['submit'])
const props = defineProps({
  // 输入框参数
  inputProps: {
    type: Object,
    disabled: false,
    default: () => ({}),
  },
  // crud组件参数
  crudProps: {
    type: Object,
    default: () => ({}),
  },
  // 本组件参数
  comProps: {
    type: Object,
    default: () => ({}),
  },
  disabled: Boolean,
  label: String,
  // 类型：add：新增 edit: 编辑 为空则为查询
  boxType: String,
  prop: String,
  // 数据类型
  dataType: String,
})

// 内置的几种组件数据
const localTypeMap = {
  // 业务类型
  business: {
    crudProps: {
      apiSource: { apiGetList: apiGetBusTypeDialogList },
      apiListParams: { orderBy: 'code', orderMethod: 'asc' },
      option: {
        searchSpan: 8,
      },
    },
  },
  // 属性类型
  attribute: {
    crudProps: {
      apiSource: { apiGetList: apiGetAttributeDialogList },
      apiListParams: { orderBy: 'code', orderMethod: 'asc' },
      option: {
        searchSpan: 8,
      },
    },
  },
  // 状态池
  statusMeta: {
    crudProps: {
      apiSource: { apiGetList: apiGetStatusMetaDialogList },
      apiListParams: { orderBy: 'code', orderMethod: 'asc' },
      option: {
        searchSpan: 8,
      },
    },
  },
  // 关键属性重复规则
  keyReatRule: {
    crudProps: {
      apiSource: { apiGetList: apiGetKeyReatDialogList },
      apiListParams: { orderBy: 'code', orderMethod: 'asc' },
      option: {
        searchSpan: 8,
        column: [
          {
            prop: 'name',
            label: '名称',
            search: true,
          },
        ],
      },
    },
  },
  // 计量单位
  unit: {
    crudProps: {
      apiSource: { apiGetList: apiGetUnitDialogList },
      apiListParams: { orderBy: 'code', orderMethod: 'asc' },
      option: {
        isTableTree: true,
        searchSpan: 8,
      },
    },
  },
  // 枚举
  enum: {
    crudProps: {
      apiSource: { apiGetList: apiGetEnumTypeDialogList },
      apiListParams: { orderBy: 'code', orderMethod: 'asc' },
      option: {
        searchSpan: 8,
      },
    },
  },
  // 部门
  dept: {
    crudProps: {
      apiSource: { apiGetList: apiGetDepDialogList },
      apiListParams: { orderBy: 'code', orderMethod: 'asc' },
      option: {
        searchSpan: 8,
      },
    },
  },
  // 人员
  user: {
    crudProps: {
      apiSource: { apiGetList: apiGetUserDialogList },
      apiListParams: { orderBy: 'code', orderMethod: 'asc' },
      option: {
        searchSpan: 8,
      },
    },
  },
}

const defaultCrudProps = {
  selectType: 'radio',
  returnType: 'object',
  autoClose: true,
  title: '选择' + props.label,
  selection: true,
  option: {
    menu: false,
    header: false,
    height: '45vh',
    reserveSelection: true, //默认可以分页选择
    treeProps: { checkStrictly: true }, //默认父子关系无联系
    column: [
      {
        prop: 'code',
        label: '编号',
        search: true,
      },
      {
        prop: 'name',
        label: '名称',
        search: true,
      },
    ],
  },
}
// 合并默认值与props值
const mergeCrudProps = computed(() => {
  let newProps = merge(
    {},
    defaultCrudProps,
    // 合并内置的数据
    localTypeMap[props.dataType]
      ? localTypeMap[props.dataType]['crudProps']
      : {},
    props.crudProps
  )
  // 如果有传入的column，则column直接使用传入数据，不对column进行合并
  if (props.crudProps?.option?.column) {
    newProps.option.column = props.crudProps?.option?.column || []
  }
  // tip必须写死false
  newProps.option.tip = false
  return newProps
})
const defaultComProps = {
  //  (优先级最低)
  // 用于保存的字段映射
  // 举例：{code: 'id'}  表示我们从取选中数据对象的id字段，但是在表单提交的时候使用code字段提交
  saveFieldMap: {},
  // (优先级最高)
  // 有时候并不是简单的赋值，可能需要的是对选中数据的字段进行拼接或者截取等操作后在赋值，所以设定自定义方法, 使用该方法会使saveField，saveFieldMap属性无效，因为所有属性赋值都应该集中在该方法处理
  // 该方法有两个参数，参数1为当前操作是保存还是清除，参数2为选中数据
  customizeFun: null,
  // 组件类型
  // form: 表单类型，适用于avue-form
  // crud: 表格类型：适用于avue-crud使用
  // direct: 直接使用，适用于直接使用，不需要crud组件
  comType: 'crud',
  // 单选
  // 用于选中回显的关键字段映射
  // 比如单选列表返回的是{id: 1},但是我使用通过saveFieldMap将id映射为了code，那么此处则应该填写{code: 'id'}
  // 多选
  // 则只填写回显的原字段，如id
  selectedKey: 'id',
  // 数据变化回调
  change: () => {},
}

// 合并自定义的组件参数和传入的组件参数
const mergeComProps = computed(() => {
  let newRes = Object.assign({}, defaultComProps, props.comProps)
  if (Array.isArray(newRes.saveFieldMap)) {
    let map = {}
    newRes.saveFieldMap.forEach((key) => {
      map[key] = key
    })
    newRes.saveFieldMap = map
  }
  return newRes
})
// 合并输入框组件的自定义参数和传入的参数
const mergeInputProps = computed(() =>
  merge(
    {},
    {
      placeholder: getPlaceholder(),
    },
    props.inputProps
  )
)
// 获取输入框的placeholder
const getPlaceholder = () => {
  if (mergeComProps.value.comType === 'crud' && !props.boxType) {
    return '请输入' + props.label
  } else if (mergeComProps.value.comType === 'direct') {
    return '请选择' + props.label
  } else {
    return '请选择' + props.label
  }
}
// 表单数据
const formData = ref({})
// 查询条件的数据
const searchData = ref('')
// 默认选中的数据
const defaultSelectList = ref([])

// 如果是直接使用该组件，则表单数据应当是从外部传入
let directFormData = defineModel('formData', {
  type: Object,
  default: () => {},
})
// 左下角展示组件之前已选中的数据
const labelShows = ref(undefined)
// 弹窗是否显示
const visible = ref(false)

onMounted(() => {
  // 回显时需要异步去获取avue组件的实例数据
  setTimeout(() => {
    labelShow()
  }, 10)
  // 监听重置按钮
  emitter.on('BasicCrudTableSelect_reset' + getbaiscCrudInstace()?.uid, () => {
    searchData.value = ''
  })
})

// 数据回显
const labelShow = () => {
  // 直接使用
  if (mergeComProps.value.comType === 'direct') {
    // 无论单选还是多选都是直接使用prop回显就行
    labelShows.value = directFormData.value[props.prop]
  }
  // 如果是avue搭配使用的
  else {
    const curInstance = getAvueFormInstance()
    formData.value = curInstance.proxy.form
    labelShows.value = formData.value[props.prop]
  }
}

// const unWatch = watch(
//   () => directFormData.value,
//   () => {
//     labelShow()
//     unWatch()
//   },
//   { deep: true }
// )

// 将表单数据转为选中的数组
const formDataToSelectList = (copyFormData, saveFieldMap) => {
  // 获取 copyFormData 中所有键值对的值并分割成数组
  const valuesArrays = {}
  for (const key in copyFormData) {
    if (key in saveFieldMap && copyFormData[key]) {
      valuesArrays[key] = copyFormData[key]?.split(',') || []
    }
  }
  // 没有选中的数据直接不走后面了
  if (Object.keys(valuesArrays).length === 0) {
    return
  }
  // 确定数组长度（假设所有值数组长度相同）
  const length = valuesArrays[Object.keys(valuesArrays)[0]].length
  const result = []
  // 遍历每个索引
  for (let i = 0; i < length; i++) {
    const newObj = {}
    // 遍历 saveFieldMapObj 中的每个映射关系
    for (const copyFormDataKey in saveFieldMap) {
      const targetKey = saveFieldMap[copyFormDataKey]
      newObj[targetKey] = valuesArrays[copyFormDataKey][i]
    }
    result.push(newObj)
  }
  return result
}

const openDialogTable = () => {
  visible.value = true
  // 选中值回显
  if (mergeComProps.value.comType !== 'direct') {
    const curInstance = getAvueFormInstance()
    formData.value = curInstance.proxy.form
  }
  // 判断comType类型从而使用不同的表单数据
  let copyFormData =
    mergeComProps.value.comType === 'direct'
      ? directFormData.value
      : formData.value
  const res = formDataToSelectList(
    copyFormData,
    mergeComProps.value.saveFieldMap
  )
  console.log(res)

  defaultSelectList.value = res || []
}

const handleSubmit = (row) => {
  setFieldVal('save', row)
  emits('submit', row)
}
// 点击取消按钮
const handleCancel = () => {
  if (mergeCrudProps.value.selectType === 'radio') {
    labelShows.value = formData.value[props.prop]
  } else if (mergeCrudProps.value.selectType === 'multiple') {
    labelShows.value = (formData.value[props.prop] || [])
      .map((item) => item[props.prop])
      .join(',')
  }
}

//crud方式使用时将输入框传入到crud实例中
const handleSearchInput = () => {
  const curInstance = getAvueFormInstance()
  curInstance.proxy.form = Object.assign({}, curInstance.proxy.form, {
    [props.prop]: searchData.value,
  })
}

// 点击清空按钮
const handleClear = () => {
  labelShows.value = ''
  setFieldVal('clear')
}

// 递归获取avue-form组件的表单数据
const getAvueFormInstance = (instace = that) => {
  if (instace?.type.name === 'avue-form') {
    return instace
  } else {
    return instace && getAvueFormInstance(instace.parent)
  }
}
// 获取basicCrud组件实例的formData
const getbaiscCrudInstace = (instace = that) => {
  if (instace?.type.__name === 'BasicCrud') {
    return instace
  } else {
    return instace && getbaiscCrudInstace(instace.parent)
  }
}

// 字段设置
// 参数1为类型save保存，clear清空，
// 参数2为选中的行数据
const setFieldVal = (type, row) => {
  const { selectType } = mergeCrudProps.value
  const { saveFieldMap, comType } = mergeComProps.value
  // 获取最新的表单数组，如果多个BasicCrudTableSelect组件使用会用到
  // if (
  //   mergeComProps.value.comType === 'form' ||
  //   mergeComProps.value.comType === 'crud'
  // ) {
  //   const curInstance = getAvueFormInstance()
  //   formData.value = curInstance.proxy.form
  // }
  // 如果是保存
  if (type === 'save') {
    // 如果是单选
    if (selectType === 'radio') {
      if (typeof mergeComProps.value.customizeFun === 'function') {
        const customResData = mergeComProps.value.customizeFun(type, row)
        formData.value = Object.assign({}, formData.value, customResData)
      } else {
        // 判断是否有字段映射
        if (Object.keys(saveFieldMap).length > 0) {
          for (const key in saveFieldMap) {
            const valKey = saveFieldMap[key]
            formData.value[key] = row[valKey]
          }
        }
      }
      labelShows.value = formData.value[props.prop]
      // 多选
    } else if (selectType === 'multiple') {
      if (typeof mergeComProps.value.customizeFun === 'function') {
        const customResData = mergeComProps.value.customizeFun(type, row)
        formData.value = Object.assign({}, formData.value, customResData)
      } else {
        if (Object.keys(saveFieldMap).length > 0) {
          for (const key in saveFieldMap) {
            const valKey = saveFieldMap[key]
            formData.value[key] = row.map((item) => item[valKey]).join(',')
          }
        }
      }
      labelShows.value = formData.value[props.prop]
    }
    // 清除数据
  } else if (type === 'clear') {
    if (typeof mergeComProps.value.customizeFun === 'function') {
      const customResData = mergeComProps.value.customizeFun(type, row)
      formData.value = Object.assign({}, formData.value, customResData)
    } else {
      // 判断是否有字段映射
      if (Object.keys(saveFieldMap).length > 0) {
        for (const key in saveFieldMap) {
          formData.value[key] = undefined
        }
      }
    }
    labelShows.value = ''
  }
  if (comType === 'crud' || comType === 'form') {
    const curInstance = getAvueFormInstance()
    curInstance.proxy.form = Object.assign(
      {},
      curInstance.proxy.form,
      formData.value
    )
    mergeComProps.value?.change(curInstance.proxy.form, row)
  } else if (comType === 'direct') {
    directFormData.value = Object.assign(
      {},
      directFormData.value,
      formData.value
    )
    mergeComProps.value?.change(directFormData.value, row)
  }
}

// 取消选中
const handleClose = (item) => {
  let selectedKey = mergeComProps.value.selectedKey
  defaultSelectList.value = defaultSelectList.value.filter((itm) => {
    if (itm[selectedKey] === item[selectedKey]) {
      return false
    } else {
      return true
    }
  })
}
</script>

<style lang="scss" scoped>
.el-tag {
  margin-right: 10px;
}
</style>
